Agenda
Announcements
- MDSR Ch 9 programming notebook assigned
- MDSR Ch 8 & 9 Exercises assigned
- midterm exam
- mix of in-class and take-home
- in-class portion Wed 2/27
- take-home portion due Fri 3/1 at 11:59pm
- class will not meet on Friday 3/1
MDSR Ch 9 Errata / Tips
- Some sections don’t require programming, but please still include the headers for navigation purposes
- p. 206: the
destfile
argument probably won’t work for you as written. You can
- (recommended) create a folder called “data” in the same directory as your .Rmd file
- delete
data/
and use destfile = fueleconomy.zip
instead. You’ll need to update subsequent functions on p. 206 and 207 in kind when attempting to access files in a subdirectory called data
- Section 9.2.2: my results aren’t identical to those in the book… maybe I made a mistake or maybe the data has changed. Namely, the clusters are similar but don’t exactly match on close inspection.
Statistical Learning (recall)
Q: What are some differences between Supervised and Unsupervised Learning?
Statistical Learning (recall)
- statistical learning refers to a vast set of tools for understanding data
- Typical distinction between Supervised and Unsupervised learning
- Supervised learning predictive or inferential modeling
- there is a response variable (Y)
- predictive modeling: we want to anticipate the response (Y) ahead of time based on knowledge of a set of measurable inputs (X’s)
- inferential modeling: we want to understand the way our response (Y) changes as the explanatory variables (X’s) change
- Unsupervised learning methods could be considered “data discovery” models
- there is NO response variable (Y)
- we are interested in exposing interesting relationships/groups/clusters among several explanatory variables (X’s)
Unsupervised Learning
- Often considered within the domain of Exploratory Data Analysis
- Search for useful structure among explanatory variables: X1, X2, …,Xp.
- Informative way to visualize the data?
- Subgroups among the variables or among the observations?
- Dimension reduction
- express low-dimensional representation of the data that explains a good fraction of the variance
- commonly used for data visualization or pre-processing before supervised learning techniques are applied
- Principle component analysis (PCA)–discussed here
- Singular value decomposition (SVD)–see MDSR Ch 9, for example
- Clustering
- broad class of methods for discovering unknown subgroups within data
- Hierarchical clustering:
- k-means: specify number of groups
- Q: when would this be useful?
Challenge of Unsupervised Learning
- No “groud truth”
- in supervised learning we can check our work by evaluating predictions using cross-validation, independent test set, etc.
- typically no mechanism to “check our work” in unsupervised learning because the true answer is unknown
Some Examples
A cancer researcher might assay gene expression levels in 100 patients with breast cancer. He or she might then look for subgroups among the breast cancer samples, or among the genes, in order to obtain a better understanding of the disease.
A search engine (e.g., Google) might choose what search results to display to a particular individual based on the click histories of other individuals with similar search patterns.
Mapping evolutionary relationships among various biological species or other entities–their phylogeny–based upon similarities and differences in their physical or genetic characteristics.
Principal Components Analysis (PCA)
- Goal: summarize a large set of correlated variables with a smaller number of representative variables that collectively explain most of the variability in the original data set
- Intuition:
- many of our variables are correlated or possibly redundant
- perhaps we can rotate our data to identify
- (PC1) the axis that maximizes variability (first principal component–PC1)
- (PC2) an axis perpendicular to PC1 that maximizes the remaining variability
- and so on… until we explain all of variability in the data
- hopefully, we can explain most (or a lot) of the total variability with only the first few principal components
- sometimes we can even attempt to interpret how principal components associate (i.e., “load”) with respect to other variables in the data
- Method: PCA can be done by eigenvalue decomposition of a data covariance (or correlation) matrix or singular value decomposition (SVD) of a data matrix, usually after a normalization step of the initial data.
US Arrests by State
- we want to compare states on these four variables
- Murder
- Assault
- Rape
- UrbanPop (% of state living in urban areas)
- Q: which variable(s) would you expect to have the largest variance
data("USArrests")
head(USArrests)
PCA: US Arrest Data
- measurement units & scales significantly impact variance
- we often want to standardize the variables first
- mean = 0; sd = 1 (convert measurements to z-scores)
- this amounts to giving each variable equal weight
- mutes effect of unit conversion (km to meters impacts variance estimate)
- might not standardize if all variables are measured on same scale
First two principal components
- Q: How do PC1 and PC2 associate with our four variables?
- Note: See what happens if we DON’T standardize variables…
USArrests_pca <- USArrests %>%
prcomp(scale = TRUE) # standardize the variables
# the result is a list object
str(USArrests_pca)
List of 5
$ sdev : num [1:4] 1.575 0.995 0.597 0.416
$ rotation: num [1:4, 1:4] -0.536 -0.583 -0.278 -0.543 0.418 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
.. ..$ : chr [1:4] "PC1" "PC2" "PC3" "PC4"
$ center : Named num [1:4] 7.79 170.76 65.54 21.23
..- attr(*, "names")= chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
$ scale : Named num [1:4] 4.36 83.34 14.47 9.37
..- attr(*, "names")= chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
$ x : num [1:50, 1:4] -0.976 -1.931 -1.745 0.14 -2.499 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:50] "Alabama" "Alaska" "Arizona" "Arkansas" ...
.. ..$ : chr [1:4] "PC1" "PC2" "PC3" "PC4"
- attr(*, "class")= chr "prcomp"
# first two principal components
(-1) * USArrests_pca$rotation[, 1:2] %>% round(2)
PC1 PC2
Murder 0.54 -0.42
Assault 0.58 -0.19
UrbanPop 0.28 0.87
Rape 0.54 0.17
# plot of the first two principal components
USArrests_pca$x %>%
as.data.frame() %>% # `ggplot2` expects a data frame object
rownames_to_column() %>%
ggplot(aes(x = -PC1, y = -PC2)) +
geom_text(aes(label = rowname), size = 3) +
xlab("Best Vector from PCA (approx. Violent Crime)") +
ylab("2nd Best Vector from PCA (approx. Urbanization)") +
ggtitle("Two-dimensional representation of US Arrests by State")

Proportion of variance explained
- (recall) Goal: summarize a large set of correlated variables with a smaller number of representative variables that collectively explain most of the variability in the original data set
- In general, a \(n \times p\) data matrix X has \(\text{min}(n-1, p)\) distince principal components
- we want smallest number of PC’s to get a good understanding of the data
- there isn’t really an optimal solution to this problem…
- Proportion of variability explained (PVE)
- Assess PVE for each of our principal component vectors
- Can we find a point of diminishing return among principal components
- we want fewest number of PC’s that still do a good job representing the data
- informally, we look for an “elbow” in the scree plot
Scree Plot (US Arrest Data)
- Q: How did we do?
- Q: How many principal components do you think we should consider?
USArrests_pve <-
data.frame(sd = USArrests_pca$sdev) %>%
rownames_to_column() %>%
mutate(rowname = parse_number(rowname),
totalVar = sum(USArrests_pca$sdev^2),
pve = 100 * sd^2 / totalVar,
cusum = cumsum(pve))
# scree plot
USArrests_pve %>%
ggplot(aes(x = rowname, y = pve)) +
geom_line(type = 3) +
xlab("Principal Component") +
ylab("Proportion of Variance Explained") +
ggtitle("Scree Plot of Principal Components for US Arrests Data")
# cumulative PVE plot
USArrests_pve %>%
ggplot(aes(x = rowname, y = cusum)) +
geom_line(type = 3) +
xlab("Principal Component") +
ylab("Proportion of Variance Explained") +
ggtitle("Cumulative Proportion of Variance Explained for US Arrests Data")
NCI60 Data Example
- The NCI-60 cancer cell line panel is a group of 60 human cancer cell lines used by the National Cancer Institute (NCI) for the screening of compounds to detect potential anticancer activity.
- Q: Why would we care about dimension reduction here? (what’s a “case” in the data?)
Inspecting the data
require(ISLR)
data("NCI60")
cancerLabels <- NCI60$labs
# recode immortalized cell lines
cancerLabels <- ifelse(test = grepl(pattern = "MCF7", x = cancerLabels),
yes = "BREAST", no = cancerLabels)
cancerLabels <- ifelse(test = grepl(pattern = "K562", x = cancerLabels),
yes = "LEUKEMIA", no = cancerLabels)
nciData <- NCI60$data
# dimensions of the data
nrow(nciData) # cases
[1] 64
ncol(nciData) # variables
[1] 6830
# inspect NCI cancer labels
head(cancerLabels)
[1] "CNS" "CNS" "CNS" "RENAL" "BREAST" "CNS"
# distribution of cancer cell lines available
tally(cancerLabels ~ 1)
1
cancerLabels 1
BREAST 9
CNS 5
COLON 7
LEUKEMIA 8
MELANOMA 8
NSCLC 9
OVARIAN 6
PROSTATE 2
RENAL 9
UNKNOWN 1
Principal components analysis of NCI60 data
- could make a case either way for standardizing variables since all metrics are on same scale in this case
- Q: How many total principal components possible?
- We have 64 PC’s this time
- US Arrests had 4 PC’s
# perform pca on scaled genes
NCI_pca <- nciData %>%
prcomp(scale = TRUE)
# the result is a list object
str(NCI_pca)
List of 5
$ sdev : num [1:64] 27.9 21.5 19.8 17 16 ...
$ rotation: num [1:6830, 1:64] -0.01068 -0.00231 -0.00588 0.00328 -0.00768 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:6830] "1" "2" "3" "4" ...
.. ..$ : chr [1:64] "PC1" "PC2" "PC3" "PC4" ...
$ center : Named num [1:6830] -0.0191 -0.0278 -0.0199 -0.3287 0.0261 ...
..- attr(*, "names")= chr [1:6830] "1" "2" "3" "4" ...
$ scale : Named num [1:6830] 0.441 0.757 0.433 1.092 0.485 ...
..- attr(*, "names")= chr [1:6830] "1" "2" "3" "4" ...
$ x : num [1:64, 1:64] -19.7 -22.9 -27.2 -42.5 -55 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:64] "V1" "V2" "V3" "V4" ...
.. ..$ : chr [1:64] "PC1" "PC2" "PC3" "PC4" ...
- attr(*, "class")= chr "prcomp"
First few principal components of NCI60 data
# plot PC1 vs PC2
NCI_pca$x %>%
as.data.frame() %>% # `ggplot2` expects a data frame object
ggplot(aes(x = PC1, y = PC2)) +
geom_point(aes(color = cancerLabels), size = 3) +
xlab("Best Vector from PCA") +
ylab("Second Best Vector from PCA") +
ggtitle("Two-dimensional representation of 6830 Genes (colored by actual cancer type)")

# plot of PC1 vs PC3
NCI_pca$x %>%
as.data.frame() %>% # `ggplot2` expects a data frame object
ggplot(aes(x = PC1, y = PC3)) +
geom_point(aes(color = cancerLabels), size = 3) +
xlab("Best Vector from PCA") +
ylab("Third Best Vector from PCA") +
ggtitle("Two-dimensional representation of 6830 Genes (colored by actual cancer type)")

# SD and variance explained by each PC
summary(NCI_pca)
Importance of components:
PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8 PC9 PC10 PC11
Standard deviation 27.853 21.4814 19.8205 17.0326 15.9718 15.7211 14.4715 13.5443 13.1440 12.7386 12.6867
Proportion of Variance 0.114 0.0676 0.0575 0.0425 0.0374 0.0362 0.0307 0.0269 0.0253 0.0238 0.0236
Cumulative Proportion 0.114 0.1812 0.2387 0.2812 0.3185 0.3547 0.3853 0.4122 0.4375 0.4613 0.4848
PC12 PC13 PC14 PC15 PC16 PC17 PC18 PC19 PC20 PC21 PC22
Standard deviation 12.1577 11.8302 11.6255 11.4378 11.0005 10.6567 10.4888 10.4352 10.3219 10.1461 10.0544
Proportion of Variance 0.0216 0.0205 0.0198 0.0192 0.0177 0.0166 0.0161 0.0159 0.0156 0.0151 0.0148
Cumulative Proportion 0.5065 0.5270 0.5467 0.5659 0.5836 0.6002 0.6163 0.6323 0.6479 0.6630 0.6778
PC23 PC24 PC25 PC26 PC27 PC28 PC29 PC30 PC31 PC32 PC33 PC34
Standard deviation 9.9027 9.6477 9.5076 9.3325 9.2732 9.0900 8.9812 8.7500 8.5996 8.4474 8.3730 8.21579
Proportion of Variance 0.0144 0.0136 0.0132 0.0127 0.0126 0.0121 0.0118 0.0112 0.0108 0.0104 0.0103 0.00988
Cumulative Proportion 0.6921 0.7057 0.7190 0.7317 0.7443 0.7564 0.7682 0.7794 0.7903 0.8007 0.8110 0.82087
PC35 PC36 PC37 PC38 PC39 PC40 PC41 PC42 PC43 PC44 PC45
Standard deviation 8.15731 7.97465 7.90446 7.82127 7.72156 7.58603 7.45619 7.3444 7.10449 7.0131 6.95839
Proportion of Variance 0.00974 0.00931 0.00915 0.00896 0.00873 0.00843 0.00814 0.0079 0.00739 0.0072 0.00709
Cumulative Proportion 0.83061 0.83992 0.84907 0.85803 0.86676 0.87518 0.88332 0.8912 0.89861 0.9058 0.91290
PC46 PC47 PC48 PC49 PC50 PC51 PC52 PC53 PC54 PC55 PC56
Standard deviation 6.8663 6.80744 6.64763 6.61607 6.40793 6.21984 6.20326 6.06706 5.91805 5.91233 5.73539
Proportion of Variance 0.0069 0.00678 0.00647 0.00641 0.00601 0.00566 0.00563 0.00539 0.00513 0.00512 0.00482
Cumulative Proportion 0.9198 0.92659 0.93306 0.93947 0.94548 0.95114 0.95678 0.96216 0.96729 0.97241 0.97723
PC57 PC58 PC59 PC60 PC61 PC62 PC63 PC64
Standard deviation 5.47261 5.2921 5.02117 4.68398 4.17567 4.08212 4.04124 2.15e-14
Proportion of Variance 0.00438 0.0041 0.00369 0.00321 0.00255 0.00244 0.00239 0.00e+00
Cumulative Proportion 0.98161 0.9857 0.98940 0.99262 0.99517 0.99761 1.00000 1.00e+00
# proportion of variance explained (PVE) of each PC
NCI_pve <-
data.frame(sd = NCI_pca$sdev) %>%
rownames_to_column() %>%
mutate(rowname = parse_number(rowname),
totalVar = sum(NCI_pca$sdev^2),
pve = 100 * sd^2 / totalVar,
cusum = cumsum(pve))
# scree plot
NCI_pve %>%
ggplot(aes(x = rowname, y = pve)) +
geom_line(type = 3) +
xlab("Principal Component") +
ylab("Proportion of Variance Explained") +
ggtitle("Scree Plot of Principal Components for NCI60 Data")
Ignoring unknown parameters: type

# cumulative PVE plot
NCI_pve %>%
ggplot(aes(x = rowname, y = cusum)) +
geom_line(type = 3) +
xlab("Principal Component") +
ylab("Proportion of Variance Explained") +
ggtitle("Cumulative Proportion of Variance Explained for NCI60 Data")
Ignoring unknown parameters: type

Clustering
- Both principal components and clustering seek to simplify the data via a small number of summaries, but the mechanisms differ
- PCA (& SVD) attempts to find a low-dimensional representation of the observations that explain a good fraction of the variance
- Clustering looks to find subgroups or define similarity among the observations
- Clustering algorithms impart organizing structure for describing degrees of similarity between different things
- Hierarchical clustering does not specify a desired number of clusters, instead maps similarity among all n (distinct) observations
- a dendogram: a tree-based organizing structure for describing/visualizing those degrees of similarity,
- doesn’t matter how those relationships came to be
- The tree may or may not reflect some deeper relationship among the objects measured
- Looks like a decision tree, but it approaches the problem from the opposite direction
- ** K-means** clustering seeks to partition observations into a pre-specified number of (K) clusters
Hierarchical clustering
- appropriate when cases are decribed by a set of numerical variables (none of which is a response)
- Method (agglomorative/bottom-up clustering):
- Begin with n cases each measured as a point in Cartesian space and calculate all \({n\choose2} = \frac{n(n-1)}{2}\) pairwise dissimilarities. Treat each observation as its own cluster.
- For \(i = n, (n-1), ..., 2\): A. Examine all pairwise inter-cluster dissimilarities among the i clusters and fuse the two clusters that are least dissimilar.
B. Compute new pairwise inter-cluster dissimilarities among the \(i-1\) remaining clusters
- for an individual quantitatve variable, it’s easy to measure distance between cases
- with multiple variables, we need to adjust for different scales and units
- no “best” solution… usually requires domain expertise
- with no other information, Euclidean distance is a sensible default (but there are others)
Distance & Linkage:
- multiple linkages are available for calculating inter-cluster dissimilarities.
- most common linkages (balanced dendograms)
- complete linkage: largest inter-cluster dissimilarity
- average linkage: mean inter-cluster dissimilarity
- less common linkages
- single linkage: smallest inter-cluster dissimilarity
- centroid linkage: dissimilarity among the centroids of clusters
Hierarchical clustering
- The tree is “grown” in reverse–from bottom to top
- The dissimilarity between clusters indicates the height in the dendogram at which the fusion should be placed.
- Q: which is more similar to obs #2?
- Q: which pair of observations is more similar?
- Where might we “cut” the dendogram to define clusters?
Cluster analysis of NCI60 data
- the rescaled variables inherited unfortunate names (just a column number)
- Q: What means & sd’s do you expect for variables
1
and 2
? (does it match?)
- clustering algorithm
- calculate all point-to-point distances (e.g., pairwise dissimilarities)
- begin by treating each point as a cluster
- iteratively fuse clusters that are least dissimilar according to linkage chosen
Hierarchical Clustering
# scale the data (centered with SD 1)
NCI_std <-
scale(nciData) %>%
as.data.frame()
# the variables have inherited some unfortunate names (just column number)
favstats(~ `1`, data = NCI_std)
favstats(~ `2`, data = NCI_std)
NCI_dist <- dist(NCI_std)
# # plot dedrogram (average linkage)
# NCI_dist %>%
# hclust(method = "average") %>%
# plot(cex = 0.9, labels = cancerLabels, main = "NCI60 Dendogram with Average Linkage")
# construct dendogram (complete linkage)
NCI_dendo <-
NCI_dist %>%
hclust(method = "complete")
# print dendogram info (distance & method)
print(NCI_dendo)
Call:
hclust(d = ., method = "complete")
Cluster method : complete
Distance : euclidean
Number of objects: 64
# plot dedrogram (complete linkage)
NCI_dendo %>%
plot(cex = 0.9, labels = cancerLabels, lwd = 2,
main = "NCI60 Dendogram with Complete Linkage")

Cut Dendogram to define clusters
- Complete dendogram can be used to produce clusters
- establish cut point based on dissimilarity index (vertical axis)
- software can choose cut based on requested number of clusters
- Q: Did we learn anything from our clusters?
# Cut dendogram to produce clusters
NCI_dendo %>%
plot(labels = cancerLabels, lwd = 2,
main = "NCI60 Dendogram with Complete Linkage (5 clusters)") %>%
abline(h = 135, col = "red", lwd = 3)

# Cut dendogram--Hierarchical clusters
NCI_DendoClusters <- cutree(tree = NCI_dendo, k = 5)
# clustering patterns (Leukemia & melanoma; not so much breast)
tally(cancerLabels ~ NCI_DendoClusters)
NCI_DendoClusters
cancerLabels 1 2 3 4 5
BREAST 0 3 0 4 2
CNS 3 2 0 0 0
COLON 2 0 0 5 0
LEUKEMIA 0 0 8 0 0
MELANOMA 2 0 0 0 6
NSCLC 7 1 0 0 1
OVARIAN 6 0 0 0 0
PROSTATE 2 0 0 0 0
RENAL 8 1 0 0 0
UNKNOWN 1 0 0 0 0
K-means clustering
- Goal: partition the observations into a pre-specified number of (K) non-overlapping clusters
- minimize within-cluster variation
- each observation is assigned to exactly one cluster
- similar to classification, but there’s no response variable, so meaning of clusters is inferred implicitly
- Method (see figure):
- Randomly assign each of the observations to clusters 1 through K
- Iterate until cluster assignments stop changing: A. For each of the K clusters, compute the cluster centroid (vector of p feature means for the observations in the kth cluster) B. Assign each observation to the cluster whose centroid is closest (e.g., in Euclidean distance)
- (strongly recommended) Run algorithm multiple times from different random initial configurations to temper impact of randomness in step 1. Argument
nstart
is available in kmeans()
function for this purpose.
- Cluster interpretation:
- remember this is part of EDA to understand structure in our data
- plot the clusters
- investigate summary statistics for the clusters
K-means Clustering
- Suppose we consider k-means with 5 clusters
- Q: How does result compare with our Hier. Clust. Dendogram?
set.seed(2)
# perform kmeans clustering (k = 5 clusters)
NCI_kmean <-
NCI_std %>%
kmeans(centers = 5, nstart = 20)
# what are we working with
str(NCI_kmean)
List of 9
$ cluster : Named int [1:64] 2 2 2 2 2 2 2 2 2 2 ...
..- attr(*, "names")= chr [1:64] "V1" "V2" "V3" "V4" ...
$ centers : num [1:5, 1:6830] 0.0205 0.2232 0.5643 -0.2946 -0.4695 ...
..- attr(*, "dimnames")=List of 2
.. ..$ : chr [1:5] "1" "2" "3" "4" ...
.. ..$ : chr [1:6830] "1" "2" "3" "4" ...
$ totss : num 430290
$ withinss : num [1:5] 37150 154545 10326 82235 44071
$ tot.withinss: num 328326
$ betweenss : num 101964
$ size : int [1:5] 9 27 4 16 8
$ iter : int 3
$ ifault : int 0
- attr(*, "class")= chr "kmeans"
# compare Hierarchical Clusters with K-Means Clusters
NCI_KMeanClusters <- NCI_kmean$cluster
# both methods match for one cluster, but others are noisier
tally(NCI_DendoClusters ~ NCI_KMeanClusters)
NCI_KMeanClusters
NCI_DendoClusters 1 2 3 4 5
1 1 20 0 10 0
2 0 7 0 0 0
3 0 0 0 0 8
4 0 0 4 5 0
5 8 0 0 1 0
Hierarchical Clustering on first 7 principal components
- How might we combine methods?
- PCA for dimension reduction
- Cluster to assess similarity
NCI_pca_hcluster <-
NCI_pca$x[, 1:7] %>%
dist() %>%
hclust()
# plot
NCI_pca_hcluster %>%
plot(labels = cancerLabels, lwd = 2,
main = "Hierarcical Clustering on First Seven Principal Components")

tally(cancerLabels ~ cutree(NCI_pca_hcluster, k = 7))
cutree(NCI_pca_hcluster, k = 7)
cancerLabels 1 2 3 4 5 6 7
BREAST 2 1 0 0 0 4 2
CNS 4 1 0 0 0 0 0
COLON 0 0 7 0 0 0 0
LEUKEMIA 0 0 0 6 2 0 0
MELANOMA 1 0 0 0 0 0 7
NSCLC 5 1 3 0 0 0 0
OVARIAN 5 1 0 0 0 0 0
PROSTATE 2 0 0 0 0 0 0
RENAL 8 1 0 0 0 0 0
UNKNOWN 0 1 0 0 0 0 0
Practical issues in clustering
- Decisions to be made…
- Standardize variables?
- Hierarchical Clustering decisions
- Which dissimilarity measure?
- What type of linkage?
- Where might we “cut” the dendogram to define clusters?
- K-means decision
- Integrity of the clusters obtained
- hard to validate clusters
- no consensus on assessing whether cluster is artifact of chance (e.g. p-value)
- sensitive to extreme observations (and multivariate outliers aren’t always easy to spot)
Recommendations
- experiment with different choices of linkage, standardized/not, etc, and look for patterns or structures that consistently emerge
- cluster random subsets of the data to get sense of robustness to outliers
- most importantly, be careful when reporting results of cluster analysis
- not absolute truth about the data (much less the population)
- it’s more of a starting point to generate scientific questions for study on (ideally) independent data
LS0tCnRpdGxlOiAiVW5zdXBlcnZpc2VkIExlYXJuaW5nIgpzdWJ0aXRsZTogIk1EU1IgQ2ggOSAmIElTTFIgQ2ggMTAiCm91dHB1dDogCiAgc2xpZHlfcHJlc2VudGF0aW9uOiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdCAgCi0tLQoKCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCm9wdGlvbnMoZGlnaXRzPTQpCgojIHBhY2thZ2VzIHVzZWQKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShJU0xSKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShnZ2RlbmRybykKCgojIHVzZXItZGVmaW5lZCBmdW5jdGlvbnMgCgoKIyBpbnB1dHMgc3VtbWFyeQpkYXRhKCJVU0FycmVzdHMiKQpkYXRhKCJOQ0k2MCIpCgpgYGAKCgojIyBBZ2VuZGEKCgojIyMjIEFubm91bmNlbWVudHMKCi0gTURTUiBDaCA5IHByb2dyYW1taW5nIG5vdGVib29rIGFzc2lnbmVkCi0gTURTUiBDaCA4ICYgOSBFeGVyY2lzZXMgYXNzaWduZWQKLSBtaWR0ZXJtIGV4YW0KICAgIC0gbWl4IG9mIGluLWNsYXNzIGFuZCB0YWtlLWhvbWUKICAgIC0gaW4tY2xhc3MgcG9ydGlvbiBXZWQgMi8yNwogICAgLSB0YWtlLWhvbWUgcG9ydGlvbiBkdWUgRnJpIDMvMSBhdCAxMTo1OXBtCiAgICAtIGNsYXNzIHdpbGwgbm90IG1lZXQgb24gRnJpZGF5IDMvMSAKCiMjIyMgTURTUiBDaCA5IEVycmF0YSAvIFRpcHMKCi0gU29tZSBzZWN0aW9ucyBkb24ndCByZXF1aXJlIHByb2dyYW1taW5nLCBidXQgcGxlYXNlIHN0aWxsIGluY2x1ZGUgdGhlIGhlYWRlcnMgZm9yIG5hdmlnYXRpb24gcHVycG9zZXMKLSBwLiAyMDY6IHRoZSBgZGVzdGZpbGVgIGFyZ3VtZW50IHByb2JhYmx5IHdvbid0IHdvcmsgZm9yIHlvdSBhcyB3cml0dGVuLiAgWW91IGNhbgogICAgMS4gKHJlY29tbWVuZGVkKSBjcmVhdGUgYSBmb2xkZXIgY2FsbGVkICJkYXRhIiBpbiB0aGUgc2FtZSBkaXJlY3RvcnkgYXMgeW91ciAuUm1kIGZpbGUKICAgIDIuIGRlbGV0ZSBgZGF0YS9gIGFuZCB1c2UgYGRlc3RmaWxlID0gZnVlbGVjb25vbXkuemlwYCBpbnN0ZWFkLiAgWW91J2xsIG5lZWQgdG8gdXBkYXRlIHN1YnNlcXVlbnQgZnVuY3Rpb25zIG9uIHAuIDIwNiBhbmQgMjA3IGluIGtpbmQgd2hlbiBhdHRlbXB0aW5nIHRvIGFjY2VzcyBmaWxlcyBpbiBhIHN1YmRpcmVjdG9yeSBjYWxsZWQgYGRhdGFgCi0gU2VjdGlvbiA5LjIuMjogbXkgcmVzdWx0cyBhcmVuJ3QgaWRlbnRpY2FsIHRvIHRob3NlIGluIHRoZSBib29rLi4uIG1heWJlIEkgbWFkZSBhIG1pc3Rha2Ugb3IgbWF5YmUgdGhlIGRhdGEgaGFzIGNoYW5nZWQuICBOYW1lbHksIHRoZSBjbHVzdGVycyBhcmUgc2ltaWxhciBidXQgZG9uJ3QgKmV4YWN0bHkqIG1hdGNoIG9uIGNsb3NlIGluc3BlY3Rpb24uICAKCgojIyBTdGF0aXN0aWNhbCBMZWFybmluZyAocmVjYWxsKQoKUTogV2hhdCBhcmUgc29tZSBkaWZmZXJlbmNlcyBiZXR3ZWVuICoqU3VwZXJ2aXNlZCoqIGFuZCAqKlVuc3VwZXJ2aXNlZCoqIExlYXJuaW5nPwoKCgojIyBTdGF0aXN0aWNhbCBMZWFybmluZyAocmVjYWxsKQoKLSBzdGF0aXN0aWNhbCBsZWFybmluZyByZWZlcnMgdG8gYSB2YXN0IHNldCBvZiB0b29scyBmb3IgdW5kZXJzdGFuZGluZyBkYXRhCi0gVHlwaWNhbCBkaXN0aW5jdGlvbiBiZXR3ZWVuICoqU3VwZXJ2aXNlZCoqIGFuZCAqKlVuc3VwZXJ2aXNlZCoqIGxlYXJuaW5nCiAgICAtICoqU3VwZXJ2aXNlZCoqIGxlYXJuaW5nIHByZWRpY3RpdmUgb3IgaW5mZXJlbnRpYWwgbW9kZWxpbmcKICAgICAgICAtIHRoZXJlIGlzIGEgcmVzcG9uc2UgdmFyaWFibGUgKFkpCiAgICAgICAgLSBwcmVkaWN0aXZlIG1vZGVsaW5nOiB3ZSB3YW50IHRvIGFudGljaXBhdGUgdGhlIHJlc3BvbnNlIChZKSBhaGVhZCBvZiB0aW1lIGJhc2VkIG9uIGtub3dsZWRnZSBvZiBhIHNldCBvZiBtZWFzdXJhYmxlIGlucHV0cyAoWCdzKQogICAgICAgIC0gaW5mZXJlbnRpYWwgbW9kZWxpbmc6IHdlIHdhbnQgdG8gdW5kZXJzdGFuZCB0aGUgd2F5IG91ciByZXNwb25zZSAoWSkgY2hhbmdlcyBhcyB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIChYJ3MpIGNoYW5nZQogICAgLSAqKlVuc3VwZXJ2aXNlZCoqIGxlYXJuaW5nIG1ldGhvZHMgY291bGQgYmUgY29uc2lkZXJlZCAiZGF0YSBkaXNjb3ZlcnkiIG1vZGVscyAKICAgICAgICAtIHRoZXJlIGlzIE5PIHJlc3BvbnNlIHZhcmlhYmxlIChZKQogICAgICAgIC0gd2UgYXJlIGludGVyZXN0ZWQgaW4gZXhwb3NpbmcgaW50ZXJlc3RpbmcgcmVsYXRpb25zaGlwcy9ncm91cHMvY2x1c3RlcnMgYW1vbmcgc2V2ZXJhbCBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKFgncykKCgojIyBVbnN1cGVydmlzZWQgTGVhcm5pbmcgCgotIE9mdGVuIGNvbnNpZGVyZWQgd2l0aGluIHRoZSBkb21haW4gb2YgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwogICAgLSBTZWFyY2ggZm9yIHVzZWZ1bCBzdHJ1Y3R1cmUgYW1vbmcgZXhwbGFuYXRvcnkgdmFyaWFibGVzOiBYMSwgWDIsIC4uLixYcC4gCiAgICAtIEluZm9ybWF0aXZlIHdheSB0byB2aXN1YWxpemUgdGhlIGRhdGE/IAogICAgLSBTdWJncm91cHMgYW1vbmcgdGhlIHZhcmlhYmxlcyBvciBhbW9uZyB0aGUgb2JzZXJ2YXRpb25zPyAgCi0gKipEaW1lbnNpb24gcmVkdWN0aW9uKioKICAgIC0gZXhwcmVzcyBsb3ctZGltZW5zaW9uYWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRhdGEgdGhhdCBleHBsYWlucyBhIGdvb2QgZnJhY3Rpb24gb2YgdGhlIHZhcmlhbmNlCiAgICAtIGNvbW1vbmx5IHVzZWQgZm9yIGRhdGEgdmlzdWFsaXphdGlvbiBvciBwcmUtcHJvY2Vzc2luZyBiZWZvcmUgc3VwZXJ2aXNlZCBsZWFybmluZyB0ZWNobmlxdWVzIGFyZSBhcHBsaWVkCiAgICAtIFByaW5jaXBsZSBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSktLWRpc2N1c3NlZCBoZXJlCiAgICAtIFNpbmd1bGFyIHZhbHVlIGRlY29tcG9zaXRpb24gKFNWRCktLXNlZSBNRFNSIENoIDksIGZvciBleGFtcGxlCi0gKipDbHVzdGVyaW5nKioKICAgIC0gYnJvYWQgY2xhc3Mgb2YgbWV0aG9kcyBmb3IgKipkaXNjb3ZlcmluZyB1bmtub3duIHN1Ymdyb3VwcyoqIHdpdGhpbiBkYXRhCiAgICAtIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nOiAKICAgIC0gay1tZWFuczogc3BlY2lmeSBudW1iZXIgb2YgZ3JvdXBzCi0gUTogd2hlbiB3b3VsZCB0aGlzIGJlIHVzZWZ1bD8KCgojIyBDaGFsbGVuZ2Ugb2YgVW5zdXBlcnZpc2VkIExlYXJuaW5nCgotIE5vICJncm91ZCB0cnV0aCIKLSBpbiBzdXBlcnZpc2VkIGxlYXJuaW5nIHdlIGNhbiBjaGVjayBvdXIgd29yayBieSBldmFsdWF0aW5nIHByZWRpY3Rpb25zIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24sIGluZGVwZW5kZW50IHRlc3Qgc2V0LCBldGMuCi0gdHlwaWNhbGx5IG5vIG1lY2hhbmlzbSB0byAiY2hlY2sgb3VyIHdvcmsiIGluIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBiZWNhdXNlIHRoZSB0cnVlIGFuc3dlciBpcyB1bmtub3duCgoKIyMgU29tZSBFeGFtcGxlcyAKCi0gQSBjYW5jZXIgcmVzZWFyY2hlciBtaWdodCBhc3NheSBnZW5lIGV4cHJlc3Npb24gbGV2ZWxzIGluIDEwMCBwYXRpZW50cyB3aXRoIGJyZWFzdCBjYW5jZXIuIEhlIG9yIHNoZSBtaWdodCB0aGVuIGxvb2sgZm9yIHN1Ymdyb3VwcyBhbW9uZyB0aGUgYnJlYXN0IGNhbmNlciBzYW1wbGVzLCBvciBhbW9uZyB0aGUgZ2VuZXMsIGluIG9yZGVyIHRvIG9idGFpbiBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkaXNlYXNlLiAKCi0gQSBzZWFyY2ggZW5naW5lIChlLmcuLCBHb29nbGUpIG1pZ2h0IGNob29zZSB3aGF0IHNlYXJjaCByZXN1bHRzIHRvIGRpc3BsYXkgdG8gYSBwYXJ0aWN1bGFyIGluZGl2aWR1YWwgYmFzZWQgb24gdGhlIGNsaWNrIGhpc3RvcmllcyBvZiBvdGhlciBpbmRpdmlkdWFscyB3aXRoIHNpbWlsYXIgc2VhcmNoIHBhdHRlcm5zLiAKCi0gTWFwcGluZyBldm9sdXRpb25hcnkgcmVsYXRpb25zaGlwcyBhbW9uZyB2YXJpb3VzIGJpb2xvZ2ljYWwgc3BlY2llcyBvciBvdGhlciBlbnRpdGllcy0tdGhlaXIgcGh5bG9nZW55LS1iYXNlZCB1cG9uIHNpbWlsYXJpdGllcyBhbmQgZGlmZmVyZW5jZXMgaW4gdGhlaXIgcGh5c2ljYWwgb3IgZ2VuZXRpYyBjaGFyYWN0ZXJpc3RpY3MuCgoKIyMgUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMgKFBDQSkKCi0gKipHb2FsKio6IHN1bW1hcml6ZSBhIGxhcmdlIHNldCBvZiBjb3JyZWxhdGVkIHZhcmlhYmxlcyB3aXRoIGEgc21hbGxlciBudW1iZXIgb2YgcmVwcmVzZW50YXRpdmUgdmFyaWFibGVzIHRoYXQgY29sbGVjdGl2ZWx5IGV4cGxhaW4gbW9zdCBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIG9yaWdpbmFsIGRhdGEgc2V0Ci0gKipJbnR1aXRpb24qKjoKICAgIC0gbWFueSBvZiBvdXIgdmFyaWFibGVzIGFyZSBjb3JyZWxhdGVkIG9yIHBvc3NpYmx5IHJlZHVuZGFudAogICAgLSBwZXJoYXBzIHdlIGNhbiByb3RhdGUgb3VyIGRhdGEgdG8gaWRlbnRpZnkgCiAgICAgICAgMS4gKFBDMSkgdGhlIGF4aXMgdGhhdCBtYXhpbWl6ZXMgdmFyaWFiaWxpdHkgKGZpcnN0IHByaW5jaXBhbCBjb21wb25lbnQtLVBDMSkKICAgICAgICAyLiAoUEMyKSBhbiBheGlzIHBlcnBlbmRpY3VsYXIgdG8gUEMxIHRoYXQgbWF4aW1pemVzIHRoZSByZW1haW5pbmcgdmFyaWFiaWxpdHkKICAgICAgICAzLiBhbmQgc28gb24uLi4gdW50aWwgd2UgZXhwbGFpbiBhbGwgb2YgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEKICAgIC0gaG9wZWZ1bGx5LCB3ZSBjYW4gZXhwbGFpbiBtb3N0IChvciBhIGxvdCkgb2YgdGhlIHRvdGFsIHZhcmlhYmlsaXR5IHdpdGggb25seSB0aGUgKipmaXJzdCBmZXcqKiBwcmluY2lwYWwgY29tcG9uZW50cwogICAgLSBzb21ldGltZXMgd2UgY2FuIGV2ZW4gYXR0ZW1wdCB0byBpbnRlcnByZXQgaG93IHByaW5jaXBhbCBjb21wb25lbnRzIGFzc29jaWF0ZSAoaS5lLiwgImxvYWQiKSB3aXRoIHJlc3BlY3QgdG8gb3RoZXIgdmFyaWFibGVzIGluIHRoZSBkYXRhIAotICoqTWV0aG9kKio6IFBDQSBjYW4gYmUgZG9uZSBieSBlaWdlbnZhbHVlIGRlY29tcG9zaXRpb24gb2YgYSBkYXRhIGNvdmFyaWFuY2UgKG9yIGNvcnJlbGF0aW9uKSBtYXRyaXggb3Igc2luZ3VsYXIgdmFsdWUgZGVjb21wb3NpdGlvbiAoU1ZEKSBvZiBhIGRhdGEgbWF0cml4LCB1c3VhbGx5IGFmdGVyIGEgbm9ybWFsaXphdGlvbiBzdGVwIG9mIHRoZSBpbml0aWFsIGRhdGEuIAoKIVtpbWFnZSBjcmVkaXQ6IEphbWVzIGV0IGFsICgyMDEzKSA8aHR0cDovL3d3dy1iY2YudXNjLmVkdS9+Z2FyZXRoL0lTTC8+IEZpZyA2LjE0XShwY2EtSVNMUi02LTE0LnBuZykKCiMjIFVTIEFycmVzdHMgYnkgU3RhdGUKCi0gd2Ugd2FudCB0byBjb21wYXJlIHN0YXRlcyBvbiB0aGVzZSBmb3VyIHZhcmlhYmxlcyAKICAgIC0gTXVyZGVyCiAgICAtIEFzc2F1bHQKICAgIC0gUmFwZQogICAgLSBVcmJhblBvcCAoJSBvZiBzdGF0ZSBsaXZpbmcgaW4gdXJiYW4gYXJlYXMpCi0gUTogd2hpY2ggdmFyaWFibGUocykgd291bGQgeW91IGV4cGVjdCB0byBoYXZlIHRoZSBsYXJnZXN0ICoqdmFyaWFuY2UqKgoKYGBge3J9CmRhdGEoIlVTQXJyZXN0cyIpCmhlYWQoVVNBcnJlc3RzKQpgYGAKCiMjIFBDQTogVVMgQXJyZXN0IERhdGEKCi0gbWVhc3VyZW1lbnQgdW5pdHMgJiBzY2FsZXMgc2lnbmlmaWNhbnRseSBpbXBhY3QgdmFyaWFuY2UKLSB3ZSBvZnRlbiB3YW50IHRvIHN0YW5kYXJkaXplIHRoZSB2YXJpYWJsZXMgZmlyc3QgCiAgICAtIG1lYW4gPSAwOyBzZCA9IDEgKGNvbnZlcnQgbWVhc3VyZW1lbnRzIHRvIHotc2NvcmVzKQogICAgLSB0aGlzIGFtb3VudHMgdG8gZ2l2aW5nIGVhY2ggdmFyaWFibGUgZXF1YWwgd2VpZ2h0CiAgICAtIG11dGVzIGVmZmVjdCBvZiB1bml0IGNvbnZlcnNpb24gKGttIHRvIG1ldGVycyBpbXBhY3RzIHZhcmlhbmNlIGVzdGltYXRlKQotIG1pZ2h0IG5vdCBzdGFuZGFyZGl6ZSBpZiBhbGwgdmFyaWFibGVzIGFyZSBtZWFzdXJlZCBvbiBzYW1lIHNjYWxlCgoKIyMjIyBGaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMKCi0gUTogSG93IGRvIFBDMSBhbmQgUEMyIGFzc29jaWF0ZSB3aXRoIG91ciBmb3VyIHZhcmlhYmxlcz8gCi0gTm90ZTogU2VlIHdoYXQgaGFwcGVucyBpZiB3ZSBET04nVCBzdGFuZGFyZGl6ZSB2YXJpYWJsZXMuLi4KCmBgYHtyfQpVU0FycmVzdHNfcGNhIDwtIFVTQXJyZXN0cyAlPiUKICBwcmNvbXAoc2NhbGUgPSBUUlVFKSAgIyBzdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzCgojIHRoZSByZXN1bHQgaXMgYSBsaXN0IG9iamVjdApzdHIoVVNBcnJlc3RzX3BjYSkKCiMgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzCigtMSkgKiBVU0FycmVzdHNfcGNhJHJvdGF0aW9uWywgMToyXSAlPiUgcm91bmQoMikKYGBgCgpgYGB7cn0KIyBwbG90IG9mIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMKVVNBcnJlc3RzX3BjYSR4ICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgICMgYGdncGxvdDJgIGV4cGVjdHMgYSBkYXRhIGZyYW1lIG9iamVjdAogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIGdncGxvdChhZXMoeCA9IC1QQzEsIHkgPSAtUEMyKSkgKyAKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm93bmFtZSksIHNpemUgPSAzKSArIAogIHhsYWIoIkJlc3QgVmVjdG9yIGZyb20gUENBIChhcHByb3guIFZpb2xlbnQgQ3JpbWUpIikgKyAKICB5bGFiKCIybmQgQmVzdCBWZWN0b3IgZnJvbSBQQ0EgKGFwcHJveC4gVXJiYW5pemF0aW9uKSIpICsgCiAgZ2d0aXRsZSgiVHdvLWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIG9mIFVTIEFycmVzdHMgYnkgU3RhdGUiKQoKYGBgCgoKIyMgUHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgCgotIChyZWNhbGwpICoqR29hbCoqOiBzdW1tYXJpemUgYSBsYXJnZSBzZXQgb2YgY29ycmVsYXRlZCB2YXJpYWJsZXMgd2l0aCBhIHNtYWxsZXIgbnVtYmVyIG9mIHJlcHJlc2VudGF0aXZlIHZhcmlhYmxlcyB0aGF0IGNvbGxlY3RpdmVseSBleHBsYWluIG1vc3Qgb2YgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBvcmlnaW5hbCBkYXRhIHNldAotIEluIGdlbmVyYWwsIGEgJG4gXHRpbWVzIHAkIGRhdGEgbWF0cml4ICoqWCoqIGhhcyAkXHRleHR7bWlufShuLTEsIHApJCBkaXN0aW5jZSBwcmluY2lwYWwgY29tcG9uZW50cwogICAgLSB3ZSB3YW50IHNtYWxsZXN0IG51bWJlciBvZiBQQydzIHRvIGdldCBhIGdvb2QgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGF0YQogICAgLSB0aGVyZSBpc24ndCByZWFsbHkgYW4gb3B0aW1hbCBzb2x1dGlvbiB0byB0aGlzIHByb2JsZW0uLi4KLSAqKlByb3BvcnRpb24gb2YgdmFyaWFiaWxpdHkgZXhwbGFpbmVkIChQVkUpKioKICAgIC0gQXNzZXNzIFBWRSBmb3IgZWFjaCBvZiBvdXIgcHJpbmNpcGFsIGNvbXBvbmVudCB2ZWN0b3JzCiAgICAtIENhbiB3ZSBmaW5kIGEgcG9pbnQgb2YgZGltaW5pc2hpbmcgcmV0dXJuIGFtb25nIHByaW5jaXBhbCBjb21wb25lbnRzCiAgICAgICAgLSB3ZSB3YW50IGZld2VzdCBudW1iZXIgb2YgUEMncyB0aGF0IHN0aWxsIGRvIGEgZ29vZCBqb2IgcmVwcmVzZW50aW5nIHRoZSBkYXRhCiAgICAgICAgLSBpbmZvcm1hbGx5LCB3ZSBsb29rIGZvciBhbiAiZWxib3ciIGluIHRoZSAqKnNjcmVlIHBsb3QqKgoKIVtCeSBLZXZpbiBMZW56ICh0YWxrIGNvbnRyaWJzKSAtIE93biB3b3JrLCBDQyBCWS1TQSAyLjUsIDxodHRwczovL2NvbW1vbnMud2lraW1lZGlhLm9yZy93L2luZGV4LnBocD9jdXJpZD0xMjM5NzQyPl0oc2NyZWUuanBnKQoKIyMgU2NyZWUgUGxvdCAoVVMgQXJyZXN0IERhdGEpCgotIFE6IEhvdyBkaWQgd2UgZG8/Ci0gUTogSG93IG1hbnkgcHJpbmNpcGFsIGNvbXBvbmVudHMgZG8geW91IHRoaW5rIHdlIHNob3VsZCBjb25zaWRlcj8KCgpgYGB7ciBldmFsPUZBTFNFfQpVU0FycmVzdHNfcHZlIDwtIAogIGRhdGEuZnJhbWUoc2QgPSBVU0FycmVzdHNfcGNhJHNkZXYpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIG11dGF0ZShyb3duYW1lID0gcGFyc2VfbnVtYmVyKHJvd25hbWUpLCAKICAgICAgICAgdG90YWxWYXIgPSBzdW0oVVNBcnJlc3RzX3BjYSRzZGV2XjIpLCAKICAgICAgICAgcHZlID0gMTAwICogc2ReMiAvIHRvdGFsVmFyLCAKICAgICAgICAgY3VzdW0gPSBjdW1zdW0ocHZlKSkKCiMgc2NyZWUgcGxvdApVU0FycmVzdHNfcHZlICU+JQogIGdncGxvdChhZXMoeCA9IHJvd25hbWUsIHkgPSBwdmUpKSArIAogIGdlb21fbGluZSh0eXBlID0gMykgKyAKICB4bGFiKCJQcmluY2lwYWwgQ29tcG9uZW50IikgKyAKICB5bGFiKCJQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCIpICsgCiAgZ2d0aXRsZSgiU2NyZWUgUGxvdCBvZiBQcmluY2lwYWwgQ29tcG9uZW50cyBmb3IgVVMgQXJyZXN0cyBEYXRhIikgCgogIAojIGN1bXVsYXRpdmUgUFZFIHBsb3QKVVNBcnJlc3RzX3B2ZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByb3duYW1lLCB5ID0gY3VzdW0pKSArIAogIGdlb21fbGluZSh0eXBlID0gMykgKyAKICB4bGFiKCJQcmluY2lwYWwgQ29tcG9uZW50IikgKyAKICB5bGFiKCJQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCIpICsgCiAgZ2d0aXRsZSgiQ3VtdWxhdGl2ZSBQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCBmb3IgVVMgQXJyZXN0cyBEYXRhIikgCgpgYGAKCgoKIyMgTkNJNjAgRGF0YSBFeGFtcGxlCgotIFRoZSBOQ0ktNjAgY2FuY2VyIGNlbGwgbGluZSBwYW5lbCBpcyBhIGdyb3VwIG9mIDYwIGh1bWFuIGNhbmNlciBjZWxsIGxpbmVzIHVzZWQgYnkgdGhlIE5hdGlvbmFsIENhbmNlciBJbnN0aXR1dGUgKE5DSSkgZm9yIHRoZSBzY3JlZW5pbmcgb2YgY29tcG91bmRzIHRvIGRldGVjdCBwb3RlbnRpYWwgYW50aWNhbmNlciBhY3Rpdml0eS4KICAgIC0gPGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL05DSS02MD4KICAgIC0gNjQgY2FuY2VyIGNlbGwgbGluZXMKICAgIC0gNjgzMCBnZW5lIGV4cHJlc3Npb24gbWVhc3VyZW1lbnRzCi0gUTogV2h5IHdvdWxkIHdlIGNhcmUgYWJvdXQgZGltZW5zaW9uIHJlZHVjdGlvbiBoZXJlPyAod2hhdCdzIGEgImNhc2UiIGluIHRoZSBkYXRhPykKCgoKIyMgSW5zcGVjdGluZyB0aGUgZGF0YQoKYGBge3J9CnJlcXVpcmUoSVNMUikKZGF0YSgiTkNJNjAiKQoKY2FuY2VyTGFiZWxzIDwtIE5DSTYwJGxhYnMgCgojIHJlY29kZSBpbW1vcnRhbGl6ZWQgY2VsbCBsaW5lcwpjYW5jZXJMYWJlbHMgPC0gaWZlbHNlKHRlc3QgPSBncmVwbChwYXR0ZXJuID0gIk1DRjciLCB4ID0gY2FuY2VyTGFiZWxzKSwKICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiQlJFQVNUIiwgbm8gPSBjYW5jZXJMYWJlbHMpCmNhbmNlckxhYmVscyA8LSBpZmVsc2UodGVzdCA9IGdyZXBsKHBhdHRlcm4gPSAiSzU2MiIsIHggPSBjYW5jZXJMYWJlbHMpLAogICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJMRVVLRU1JQSIsIG5vID0gY2FuY2VyTGFiZWxzKQoKbmNpRGF0YSA8LSBOQ0k2MCRkYXRhIAoKIyBkaW1lbnNpb25zIG9mIHRoZSBkYXRhCm5yb3cobmNpRGF0YSkgICMgY2FzZXMKbmNvbChuY2lEYXRhKSAgIyB2YXJpYWJsZXMKCiMgaW5zcGVjdCBOQ0kgY2FuY2VyIGxhYmVscwpoZWFkKGNhbmNlckxhYmVscykKCiMgZGlzdHJpYnV0aW9uIG9mIGNhbmNlciBjZWxsIGxpbmVzIGF2YWlsYWJsZQp0YWxseShjYW5jZXJMYWJlbHMgfiAxKQoKYGBgCgo8IS0tIERheTIgLS0+CgojIyBQcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyBvZiBOQ0k2MCBkYXRhCgotIGNvdWxkIG1ha2UgYSBjYXNlIGVpdGhlciB3YXkgZm9yIHN0YW5kYXJkaXppbmcgdmFyaWFibGVzIHNpbmNlIGFsbCBtZXRyaWNzIGFyZSBvbiBzYW1lIHNjYWxlIGluIHRoaXMgY2FzZQotIFE6IEhvdyBtYW55IHRvdGFsIHByaW5jaXBhbCBjb21wb25lbnRzIHBvc3NpYmxlPwogICAgLSBXZSBoYXZlIDY0IFBDJ3MgdGhpcyB0aW1lCiAgICAtIFVTIEFycmVzdHMgaGFkIDQgUEMncwoKYGBge3J9CiMgcGVyZm9ybSBwY2Egb24gc2NhbGVkIGdlbmVzCk5DSV9wY2EgPC0gbmNpRGF0YSAlPiUKICBwcmNvbXAoc2NhbGUgPSBUUlVFKSAgCgojIHRoZSByZXN1bHQgaXMgYSBsaXN0IG9iamVjdApzdHIoTkNJX3BjYSkKYGBgCgoKIyMgRmlyc3QgZmV3IHByaW5jaXBhbCBjb21wb25lbnRzIG9mIE5DSTYwIGRhdGEKCmBgYHtyfQojIHBsb3QgUEMxIHZzIFBDMgpOQ0lfcGNhJHggJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAgIyBgZ2dwbG90MmAgZXhwZWN0cyBhIGRhdGEgZnJhbWUgb2JqZWN0CiAgZ2dwbG90KGFlcyh4ID0gUEMxLCB5ID0gUEMyKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhbmNlckxhYmVscyksIHNpemUgPSAzKSArIAogIHhsYWIoIkJlc3QgVmVjdG9yIGZyb20gUENBIikgKyAKICB5bGFiKCJTZWNvbmQgQmVzdCBWZWN0b3IgZnJvbSBQQ0EiKSArIAogIGdndGl0bGUoIlR3by1kaW1lbnNpb25hbCByZXByZXNlbnRhdGlvbiBvZiA2ODMwIEdlbmVzIChjb2xvcmVkIGJ5IGFjdHVhbCBjYW5jZXIgdHlwZSkiKSAKCgojIHBsb3Qgb2YgUEMxIHZzIFBDMwpOQ0lfcGNhJHggJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAgIyBgZ2dwbG90MmAgZXhwZWN0cyBhIGRhdGEgZnJhbWUgb2JqZWN0CiAgZ2dwbG90KGFlcyh4ID0gUEMxLCB5ID0gUEMzKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhbmNlckxhYmVscyksIHNpemUgPSAzKSArIAogIHhsYWIoIkJlc3QgVmVjdG9yIGZyb20gUENBIikgKyAKICB5bGFiKCJUaGlyZCBCZXN0IFZlY3RvciBmcm9tIFBDQSIpICsKICBnZ3RpdGxlKCJUd28tZGltZW5zaW9uYWwgcmVwcmVzZW50YXRpb24gb2YgNjgzMCBHZW5lcyAoY29sb3JlZCBieSBhY3R1YWwgY2FuY2VyIHR5cGUpIikKYGBgCgoKCmBgYHtyfQojIFNEIGFuZCB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBQQwpzdW1tYXJ5KE5DSV9wY2EpCgojIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIChQVkUpIG9mIGVhY2ggUEMKTkNJX3B2ZSA8LSAKICBkYXRhLmZyYW1lKHNkID0gTkNJX3BjYSRzZGV2KSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBtdXRhdGUocm93bmFtZSA9IHBhcnNlX251bWJlcihyb3duYW1lKSwgCiAgICAgICAgIHRvdGFsVmFyID0gc3VtKE5DSV9wY2Ekc2Rldl4yKSwgCiAgICAgICAgIHB2ZSA9IDEwMCAqIHNkXjIgLyB0b3RhbFZhciwgCiAgICAgICAgIGN1c3VtID0gY3Vtc3VtKHB2ZSkpCgojIHNjcmVlIHBsb3QKTkNJX3B2ZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByb3duYW1lLCB5ID0gcHZlKSkgKyAKICBnZW9tX2xpbmUodHlwZSA9IDMpICsgCiAgeGxhYigiUHJpbmNpcGFsIENvbXBvbmVudCIpICsgCiAgeWxhYigiUHJvcG9ydGlvbiBvZiBWYXJpYW5jZSBFeHBsYWluZWQiKSArIAogIGdndGl0bGUoIlNjcmVlIFBsb3Qgb2YgUHJpbmNpcGFsIENvbXBvbmVudHMgZm9yIE5DSTYwIERhdGEiKSAKCiAgCiMgY3VtdWxhdGl2ZSBQVkUgcGxvdApOQ0lfcHZlICU+JQogIGdncGxvdChhZXMoeCA9IHJvd25hbWUsIHkgPSBjdXN1bSkpICsgCiAgZ2VvbV9saW5lKHR5cGUgPSAzKSArIAogIHhsYWIoIlByaW5jaXBhbCBDb21wb25lbnQiKSArIAogIHlsYWIoIlByb3BvcnRpb24gb2YgVmFyaWFuY2UgRXhwbGFpbmVkIikgKyAKICBnZ3RpdGxlKCJDdW11bGF0aXZlIFByb3BvcnRpb24gb2YgVmFyaWFuY2UgRXhwbGFpbmVkIGZvciBOQ0k2MCBEYXRhIikgCgpgYGAKCgoKCgoKIyMgQ2x1c3RlcmluZwoKLSBCb3RoIHByaW5jaXBhbCBjb21wb25lbnRzIGFuZCBjbHVzdGVyaW5nIHNlZWsgdG8gc2ltcGxpZnkgdGhlIGRhdGEgdmlhIGEgc21hbGwgbnVtYmVyIG9mIHN1bW1hcmllcywgYnV0IHRoZSBtZWNoYW5pc21zIGRpZmZlcgogICAgLSBQQ0EgKCYgU1ZEKSBhdHRlbXB0cyB0byBmaW5kIGEgbG93LWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBvYnNlcnZhdGlvbnMgdGhhdCBleHBsYWluIGEgZ29vZCBmcmFjdGlvbiBvZiB0aGUgdmFyaWFuY2UKICAgIC0gQ2x1c3RlcmluZyBsb29rcyB0byBmaW5kIHN1Ymdyb3VwcyBvciBkZWZpbmUgc2ltaWxhcml0eSBhbW9uZyB0aGUgb2JzZXJ2YXRpb25zCi0gKipDbHVzdGVyaW5nKiogYWxnb3JpdGhtcyBpbXBhcnQgb3JnYW5pemluZyBzdHJ1Y3R1cmUgZm9yIGRlc2NyaWJpbmcgZGVncmVlcyBvZiBzaW1pbGFyaXR5IGJldHdlZW4gZGlmZmVyZW50IHRoaW5ncwogICAgLSAqKkhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nKiogZG9lcyBub3Qgc3BlY2lmeSBhIGRlc2lyZWQgbnVtYmVyIG9mIGNsdXN0ZXJzLCBpbnN0ZWFkIG1hcHMgc2ltaWxhcml0eSBhbW9uZyBhbGwgKm4qIChkaXN0aW5jdCkgb2JzZXJ2YXRpb25zCiAgICAtIGEgKipkZW5kb2dyYW0qKjogYSB0cmVlLWJhc2VkIG9yZ2FuaXppbmcgc3RydWN0dXJlIGZvciBkZXNjcmliaW5nL3Zpc3VhbGl6aW5nIHRob3NlIGRlZ3JlZXMgb2Ygc2ltaWxhcml0eSwKICAgICAgLSBkb2Vzbid0IG1hdHRlciBob3cgdGhvc2UgcmVsYXRpb25zaGlwcyBjYW1lIHRvIGJlCiAgICAgIC0gVGhlIHRyZWUgbWF5IG9yIG1heSBub3QgcmVmbGVjdCBzb21lIGRlZXBlciByZWxhdGlvbnNoaXAgYW1vbmcgdGhlIG9iamVjdHMgbWVhc3VyZWQKICAgICAgLSAqTG9va3MqIGxpa2UgYSBkZWNpc2lvbiB0cmVlLCBidXQgaXQgYXBwcm9hY2hlcyB0aGUgcHJvYmxlbSBmcm9tIHRoZSBvcHBvc2l0ZSBkaXJlY3Rpb24KICAgIC0gKiogKksqLW1lYW5zKiogY2x1c3RlcmluZyBzZWVrcyB0byBwYXJ0aXRpb24gb2JzZXJ2YXRpb25zIGludG8gYSBwcmUtc3BlY2lmaWVkIG51bWJlciBvZiAoSykgY2x1c3RlcnMKCgohW2ltYWdlIGNyZWRpdDogSmFtZXMgZXQgYWwgKDIwMTMpIDxodHRwOi8vd3d3LWJjZi51c2MuZWR1L35nYXJldGgvSVNMLz4gRmlnIDEwLjEwXShkZW5kb2dyYW0tSVNMUi0xMC0xMC5wbmcpCgojIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZwoKLSBhcHByb3ByaWF0ZSB3aGVuIGNhc2VzIGFyZSBkZWNyaWJlZCBieSBhIHNldCBvZiBudW1lcmljYWwgdmFyaWFibGVzIChub25lIG9mIHdoaWNoIGlzIGEgKnJlc3BvbnNlKikKLSBNZXRob2QgKGFnZ2xvbW9yYXRpdmUvYm90dG9tLXVwIGNsdXN0ZXJpbmcpOgogICAgMS4gQmVnaW4gd2l0aCAqbiogY2FzZXMgZWFjaCBtZWFzdXJlZCBhcyBhIHBvaW50IGluIENhcnRlc2lhbiBzcGFjZSBhbmQgY2FsY3VsYXRlIGFsbCAke25cY2hvb3NlMn0gPSBcZnJhY3tuKG4tMSl9ezJ9JCBwYWlyd2lzZSBkaXNzaW1pbGFyaXRpZXMuICAqKlRyZWF0IGVhY2ggb2JzZXJ2YXRpb24gYXMgaXRzIG93biBjbHVzdGVyLioqCiAgICAyLiBGb3IgJGkgPSBuLCAobi0xKSwgLi4uLCAyJDoKICAgICAgICBBLiBFeGFtaW5lIGFsbCBwYWlyd2lzZSBpbnRlci1jbHVzdGVyIGRpc3NpbWlsYXJpdGllcyBhbW9uZyB0aGUgKmkqIGNsdXN0ZXJzIGFuZCBmdXNlIHRoZSB0d28gY2x1c3RlcnMgdGhhdCBhcmUgKipsZWFzdCBkaXNzaW1pbGFyKiouICAKICAgICAgICBCLiBDb21wdXRlIG5ldyBwYWlyd2lzZSBpbnRlci1jbHVzdGVyIGRpc3NpbWlsYXJpdGllcyBhbW9uZyB0aGUgJGktMSQgcmVtYWluaW5nIGNsdXN0ZXJzCi0gZm9yIGFuIGluZGl2aWR1YWwgcXVhbnRpdGF0dmUgdmFyaWFibGUsIGl0J3MgZWFzeSB0byBtZWFzdXJlIGRpc3RhbmNlIGJldHdlZW4gY2FzZXMKLSB3aXRoIG11bHRpcGxlIHZhcmlhYmxlcywgd2UgbmVlZCB0byBhZGp1c3QgZm9yIGRpZmZlcmVudCBzY2FsZXMgYW5kIHVuaXRzCiAgICAtIG5vICJiZXN0IiBzb2x1dGlvbi4uLiB1c3VhbGx5IHJlcXVpcmVzIGRvbWFpbiBleHBlcnRpc2UKICAgIC0gd2l0aCBubyBvdGhlciBpbmZvcm1hdGlvbiwgRXVjbGlkZWFuIGRpc3RhbmNlIGlzIGEgc2Vuc2libGUgZGVmYXVsdCAoYnV0IHRoZXJlIGFyZSBvdGhlcnMpCgohW2ltYWdlIGNyZWRpdDogSmFtZXMgZXQgYWwgKDIwMTMpIDxodHRwOi8vd3d3LWJjZi51c2MuZWR1L35nYXJldGgvSVNMLz4gRmlnIDEwLjExXShoY2x1c3QtSVNMUi0xMC0xMS5wbmcpCgoKIyMgRGlzdGFuY2UgJiBMaW5rYWdlOiAKCiFbaW1hZ2UgY3JlZGl0OiBKYW1lcyBldCBhbCAoMjAxMykgPGh0dHA6Ly93d3ctYmNmLnVzYy5lZHUvfmdhcmV0aC9JU0wvPiBGaWcgMTAuMTFdKGhjbHVzdC1JU0xSLTEwLTExLnBuZykKCi0gbXVsdGlwbGUgbGlua2FnZXMgYXJlIGF2YWlsYWJsZSBmb3IgY2FsY3VsYXRpbmcgaW50ZXItY2x1c3RlciBkaXNzaW1pbGFyaXRpZXMuIAotIG1vc3QgY29tbW9uIGxpbmthZ2VzIChiYWxhbmNlZCBkZW5kb2dyYW1zKQogICAgLSBjb21wbGV0ZSBsaW5rYWdlOiBsYXJnZXN0IGludGVyLWNsdXN0ZXIgZGlzc2ltaWxhcml0eQogICAgLSBhdmVyYWdlIGxpbmthZ2U6IG1lYW4gaW50ZXItY2x1c3RlciBkaXNzaW1pbGFyaXR5Ci0gbGVzcyBjb21tb24gbGlua2FnZXMKICAgIC0gc2luZ2xlIGxpbmthZ2U6IHNtYWxsZXN0IGludGVyLWNsdXN0ZXIgZGlzc2ltaWxhcml0eSAKICAgIC0gY2VudHJvaWQgbGlua2FnZTogZGlzc2ltaWxhcml0eSBhbW9uZyB0aGUgY2VudHJvaWRzIG9mIGNsdXN0ZXJzIAoKCiMjIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCgotIFRoZSB0cmVlIGlzICJncm93biIgaW4gcmV2ZXJzZS0tZnJvbSBib3R0b20gdG8gdG9wCi0gVGhlICoqZGlzc2ltaWxhcml0eSoqIGJldHdlZW4gY2x1c3RlcnMgaW5kaWNhdGVzIHRoZSBoZWlnaHQgaW4gdGhlIGRlbmRvZ3JhbSBhdCB3aGljaCB0aGUgZnVzaW9uIHNob3VsZCBiZSBwbGFjZWQuCiAgICAtIFE6IHdoaWNoIGlzIG1vcmUgc2ltaWxhciB0byBvYnMgIzI/CiAgICAgICAgLSBPYnMgIzcKICAgICAgICAtIE9icyAjOQogICAgLSBROiB3aGljaCBwYWlyIG9mIG9ic2VydmF0aW9ucyBpcyBtb3JlIHNpbWlsYXI/CiAgICAgICAgLSB7MywgNn0KICAgICAgICAtIHs5LCAyfQotIFdoZXJlIG1pZ2h0IHdlICJjdXQiIHRoZSBkZW5kb2dyYW0gdG8gZGVmaW5lIGNsdXN0ZXJzPwoKCiFbaW1hZ2UgY3JlZGl0OiBKYW1lcyBldCBhbCAoMjAxMykgPGh0dHA6Ly93d3ctYmNmLnVzYy5lZHUvfmdhcmV0aC9JU0wvPiBGaWcgMTAuMTBdKGRlbmRvZ3JhbS1JU0xSLTEwLTEwLnBuZykgCgo8IS0tIERheTMgLS0+CgojIyBDbHVzdGVyIGFuYWx5c2lzIG9mIE5DSTYwIGRhdGEKCi0gdGhlIHJlc2NhbGVkIHZhcmlhYmxlcyBpbmhlcml0ZWQgdW5mb3J0dW5hdGUgbmFtZXMgKGp1c3QgYSBjb2x1bW4gbnVtYmVyKQogICAgLSBROiBXaGF0IG1lYW5zICYgc2QncyBkbyB5b3UgZXhwZWN0IGZvciB2YXJpYWJsZXMgYDFgIGFuZCBgMmA/IChkb2VzIGl0IG1hdGNoPykKLSBjbHVzdGVyaW5nIGFsZ29yaXRobQogICAgLSBjYWxjdWxhdGUgYWxsIHBvaW50LXRvLXBvaW50IGRpc3RhbmNlcyAoZS5nLiwgcGFpcndpc2UgZGlzc2ltaWxhcml0aWVzKQogICAgLSBiZWdpbiBieSB0cmVhdGluZyBlYWNoIHBvaW50IGFzIGEgY2x1c3RlcgogICAgLSBpdGVyYXRpdmVseSBmdXNlIGNsdXN0ZXJzIHRoYXQgYXJlIGxlYXN0IGRpc3NpbWlsYXIgYWNjb3JkaW5nIHRvIGxpbmthZ2UgY2hvc2VuCgoKIyMjIyMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CiMgc2NhbGUgdGhlIGRhdGEgKGNlbnRlcmVkIHdpdGggU0QgMSkKTkNJX3N0ZCA8LQogIHNjYWxlKG5jaURhdGEpICU+JQogIGFzLmRhdGEuZnJhbWUoKQoKIyB0aGUgdmFyaWFibGVzIGhhdmUgaW5oZXJpdGVkIHNvbWUgdW5mb3J0dW5hdGUgbmFtZXMgKGp1c3QgY29sdW1uIG51bWJlcikKZmF2c3RhdHMofiBgMWAsIGRhdGEgPSBOQ0lfc3RkKQpmYXZzdGF0cyh+IGAyYCwgZGF0YSA9IE5DSV9zdGQpCgpOQ0lfZGlzdCA8LSBkaXN0KE5DSV9zdGQpCgojICMgcGxvdCBkZWRyb2dyYW0gKGF2ZXJhZ2UgbGlua2FnZSkKIyBOQ0lfZGlzdCAlPiUKIyAgIGhjbHVzdChtZXRob2QgPSAiYXZlcmFnZSIpICU+JQojICAgcGxvdChjZXggPSAwLjksIGxhYmVscyA9IGNhbmNlckxhYmVscywgbWFpbiA9ICJOQ0k2MCBEZW5kb2dyYW0gd2l0aCBBdmVyYWdlIExpbmthZ2UiKQoKIyBjb25zdHJ1Y3QgZGVuZG9ncmFtIChjb21wbGV0ZSBsaW5rYWdlKQpOQ0lfZGVuZG8gPC0KICBOQ0lfZGlzdCAlPiUKICBoY2x1c3QobWV0aG9kID0gImNvbXBsZXRlIikKCiMgcHJpbnQgZGVuZG9ncmFtIGluZm8gKGRpc3RhbmNlICYgbWV0aG9kKQpwcmludChOQ0lfZGVuZG8pCgojIHBsb3QgZGVkcm9ncmFtIChjb21wbGV0ZSBsaW5rYWdlKQpOQ0lfZGVuZG8gJT4lCiAgcGxvdChjZXggPSAwLjksIGxhYmVscyA9IGNhbmNlckxhYmVscywgbHdkID0gMiwKICAgICAgIG1haW4gPSAiTkNJNjAgRGVuZG9ncmFtIHdpdGggQ29tcGxldGUgTGlua2FnZSIpCgpgYGAKCgoKIyMjIyMgQ3V0IERlbmRvZ3JhbSB0byBkZWZpbmUgY2x1c3RlcnMKCi0gQ29tcGxldGUgZGVuZG9ncmFtIGNhbiBiZSB1c2VkIHRvIHByb2R1Y2UgY2x1c3RlcnMKLSBlc3RhYmxpc2ggY3V0IHBvaW50IGJhc2VkIG9uIGRpc3NpbWlsYXJpdHkgaW5kZXggKHZlcnRpY2FsIGF4aXMpCi0gc29mdHdhcmUgY2FuIGNob29zZSBjdXQgYmFzZWQgb24gcmVxdWVzdGVkIG51bWJlciBvZiBjbHVzdGVycwotIFE6IERpZCB3ZSBsZWFybiBhbnl0aGluZyBmcm9tIG91ciBjbHVzdGVycz8KCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CiMgQ3V0IGRlbmRvZ3JhbSB0byBwcm9kdWNlIGNsdXN0ZXJzCk5DSV9kZW5kbyAlPiUKICBwbG90KGxhYmVscyA9IGNhbmNlckxhYmVscywgbHdkID0gMiwKICAgICAgIG1haW4gPSAiTkNJNjAgRGVuZG9ncmFtIHdpdGggQ29tcGxldGUgTGlua2FnZSAoNSBjbHVzdGVycykiKSAlPiUKICBhYmxpbmUoaCA9IDEzNSwgY29sID0gInJlZCIsIGx3ZCA9IDMpCgojIEN1dCBkZW5kb2dyYW0tLUhpZXJhcmNoaWNhbCBjbHVzdGVycwpOQ0lfRGVuZG9DbHVzdGVycyA8LSBjdXRyZWUodHJlZSA9IE5DSV9kZW5kbywgayA9IDUpCgoKIyBjbHVzdGVyaW5nIHBhdHRlcm5zIChMZXVrZW1pYSAmIG1lbGFub21hOyBub3Qgc28gbXVjaCBicmVhc3QpCnRhbGx5KGNhbmNlckxhYmVscyB+IE5DSV9EZW5kb0NsdXN0ZXJzKQoKCmBgYAoKCgojIyBLLW1lYW5zIGNsdXN0ZXJpbmcKCi0gR29hbDogcGFydGl0aW9uIHRoZSBvYnNlcnZhdGlvbnMgaW50byBhIHByZS1zcGVjaWZpZWQgbnVtYmVyIG9mICgqSyopIG5vbi1vdmVybGFwcGluZyBjbHVzdGVycwogICAgLSBtaW5pbWl6ZSB3aXRoaW4tY2x1c3RlciB2YXJpYXRpb24KICAgIC0gZWFjaCBvYnNlcnZhdGlvbiBpcyBhc3NpZ25lZCB0byBleGFjdGx5IG9uZSBjbHVzdGVyCiAgICAtIHNpbWlsYXIgdG8gY2xhc3NpZmljYXRpb24sIGJ1dCB0aGVyZSdzIG5vIHJlc3BvbnNlIHZhcmlhYmxlLCBzbyBtZWFuaW5nIG9mIGNsdXN0ZXJzIGlzIGluZmVycmVkIGltcGxpY2l0bHkKLSBNZXRob2QgKHNlZSBmaWd1cmUpOgogICAgMS4gUmFuZG9tbHkgYXNzaWduIGVhY2ggb2YgdGhlIG9ic2VydmF0aW9ucyB0byBjbHVzdGVycyAxIHRocm91Z2ggSwogICAgMi4gSXRlcmF0ZSB1bnRpbCBjbHVzdGVyIGFzc2lnbm1lbnRzIHN0b3AgY2hhbmdpbmc6CiAgICAgICAgQS4gRm9yIGVhY2ggb2YgdGhlICpLKiBjbHVzdGVycywgY29tcHV0ZSB0aGUgY2x1c3RlciAqY2VudHJvaWQqICh2ZWN0b3Igb2YgKnAqIGZlYXR1cmUgbWVhbnMgZm9yIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlICprKnRoIGNsdXN0ZXIpCiAgICAgICAgQi4gQXNzaWduIGVhY2ggb2JzZXJ2YXRpb24gdG8gdGhlIGNsdXN0ZXIgd2hvc2UgY2VudHJvaWQgaXMgY2xvc2VzdCAoZS5nLiwgaW4gRXVjbGlkZWFuIGRpc3RhbmNlKQogICAgMy4gKHN0cm9uZ2x5IHJlY29tbWVuZGVkKSBSdW4gYWxnb3JpdGhtIG11bHRpcGxlIHRpbWVzIGZyb20gZGlmZmVyZW50IHJhbmRvbSBpbml0aWFsIGNvbmZpZ3VyYXRpb25zIHRvIHRlbXBlciBpbXBhY3Qgb2YgcmFuZG9tbmVzcyBpbiBzdGVwIDEuICBBcmd1bWVudCBgbnN0YXJ0YCBpcyBhdmFpbGFibGUgaW4gYGttZWFucygpYCBmdW5jdGlvbiBmb3IgdGhpcyBwdXJwb3NlLgotIENsdXN0ZXIgaW50ZXJwcmV0YXRpb246CiAgICAtIHJlbWVtYmVyIHRoaXMgaXMgcGFydCBvZiBFREEgdG8gdW5kZXJzdGFuZCBzdHJ1Y3R1cmUgaW4gb3VyIGRhdGEKICAgIC0gcGxvdCB0aGUgY2x1c3RlcnMKICAgIC0gaW52ZXN0aWdhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgY2x1c3RlcnMKCiFbaW1hZ2UgY3JlZGl0OiBKYW1lcyBldCBhbCAoMjAxMykgPGh0dHA6Ly93d3ctYmNmLnVzYy5lZHUvfmdhcmV0aC9JU0wvPiBGaWcgMTAuNl0oa21lYW5zLUlTTFItMTAtNi5wbmcpCgoKIyMgSy1tZWFucyBDbHVzdGVyaW5nCgotIFN1cHBvc2Ugd2UgY29uc2lkZXIgay1tZWFucyB3aXRoIDUgY2x1c3RlcnMKLSBROiBIb3cgZG9lcyByZXN1bHQgY29tcGFyZSB3aXRoIG91ciBIaWVyLiBDbHVzdC4gRGVuZG9ncmFtPwoKYGBge3J9CnNldC5zZWVkKDIpCgojIHBlcmZvcm0ga21lYW5zIGNsdXN0ZXJpbmcgKGsgPSA1IGNsdXN0ZXJzKQpOQ0lfa21lYW4gPC0KICBOQ0lfc3RkICU+JQogIGttZWFucyhjZW50ZXJzID0gNSwgbnN0YXJ0ID0gMjApCgojIHdoYXQgYXJlIHdlIHdvcmtpbmcgd2l0aApzdHIoTkNJX2ttZWFuKQoKIyBjb21wYXJlIEhpZXJhcmNoaWNhbCBDbHVzdGVycyB3aXRoIEstTWVhbnMgQ2x1c3RlcnMKTkNJX0tNZWFuQ2x1c3RlcnMgPC0gTkNJX2ttZWFuJGNsdXN0ZXIKCiMgYm90aCBtZXRob2RzIG1hdGNoIGZvciBvbmUgY2x1c3RlciwgYnV0IG90aGVycyBhcmUgbm9pc2llcgp0YWxseShOQ0lfRGVuZG9DbHVzdGVycyB+IE5DSV9LTWVhbkNsdXN0ZXJzKQoKYGBgCgoKIyMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgb24gZmlyc3QgNyBwcmluY2lwYWwgY29tcG9uZW50cwoKLSBIb3cgbWlnaHQgd2UgY29tYmluZSBtZXRob2RzPwogICAgLSBQQ0EgZm9yIGRpbWVuc2lvbiByZWR1Y3Rpb24KICAgIC0gQ2x1c3RlciB0byBhc3Nlc3Mgc2ltaWxhcml0eQoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9Nn0KTkNJX3BjYV9oY2x1c3RlciA8LQogIE5DSV9wY2EkeFssIDE6N10gJT4lCiAgZGlzdCgpICU+JQogIGhjbHVzdCgpCgojIHBsb3QKTkNJX3BjYV9oY2x1c3RlciAlPiUKICBwbG90KGxhYmVscyA9IGNhbmNlckxhYmVscywgbHdkID0gMiwKICAgICAgIG1haW4gPSAiSGllcmFyY2ljYWwgQ2x1c3RlcmluZyBvbiBGaXJzdCBTZXZlbiBQcmluY2lwYWwgQ29tcG9uZW50cyIpCgp0YWxseShjYW5jZXJMYWJlbHMgfiBjdXRyZWUoTkNJX3BjYV9oY2x1c3RlciwgayA9IDcpKQpgYGAKCgoKIyMgUHJhY3RpY2FsIGlzc3VlcyBpbiBjbHVzdGVyaW5nCgotIERlY2lzaW9ucyB0byBiZSBtYWRlLi4uCiAgICAtIFN0YW5kYXJkaXplIHZhcmlhYmxlcz8KICAgIC0gSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgZGVjaXNpb25zCiAgICAgICAgLSBXaGljaCBkaXNzaW1pbGFyaXR5IG1lYXN1cmU/CiAgICAgICAgLSBXaGF0IHR5cGUgb2YgbGlua2FnZT8KICAgICAgICAtIFdoZXJlIG1pZ2h0IHdlICJjdXQiIHRoZSBkZW5kb2dyYW0gdG8gZGVmaW5lIGNsdXN0ZXJzPwogICAgLSBLLW1lYW5zIGRlY2lzaW9uCiAgICAgICAgLSBob3cgdG8gY2hvb3NlIEs/Ci0gSW50ZWdyaXR5IG9mIHRoZSBjbHVzdGVycyBvYnRhaW5lZAogICAgLSBoYXJkIHRvIHZhbGlkYXRlIGNsdXN0ZXJzCiAgICAtIG5vIGNvbnNlbnN1cyBvbiBhc3Nlc3Npbmcgd2hldGhlciBjbHVzdGVyIGlzIGFydGlmYWN0IG9mIGNoYW5jZSAoZS5nLiBwLXZhbHVlKQogICAgLSBzZW5zaXRpdmUgdG8gZXh0cmVtZSBvYnNlcnZhdGlvbnMgKGFuZCBtdWx0aXZhcmlhdGUgb3V0bGllcnMgYXJlbid0IGFsd2F5cyBlYXN5IHRvIHNwb3QpCgojIyMjIFJlY29tbWVuZGF0aW9ucwoKLSBleHBlcmltZW50IHdpdGggZGlmZmVyZW50IGNob2ljZXMgb2YgbGlua2FnZSwgc3RhbmRhcmRpemVkL25vdCwgZXRjLCBhbmQgbG9vayBmb3IgcGF0dGVybnMgb3Igc3RydWN0dXJlcyB0aGF0IGNvbnNpc3RlbnRseSBlbWVyZ2UKLSBjbHVzdGVyIHJhbmRvbSBzdWJzZXRzIG9mIHRoZSBkYXRhIHRvIGdldCBzZW5zZSBvZiByb2J1c3RuZXNzIHRvIG91dGxpZXJzCi0gbW9zdCBpbXBvcnRhbnRseSwgYmUgY2FyZWZ1bCB3aGVuIHJlcG9ydGluZyByZXN1bHRzIG9mIGNsdXN0ZXIgYW5hbHlzaXMKICAgIC0gbm90IGFic29sdXRlIHRydXRoIGFib3V0IHRoZSBkYXRhIChtdWNoIGxlc3MgdGhlIHBvcHVsYXRpb24pCiAgICAtIGl0J3MgbW9yZSBvZiBhIHN0YXJ0aW5nIHBvaW50IHRvIGdlbmVyYXRlIHNjaWVudGlmaWMgcXVlc3Rpb25zIGZvciBzdHVkeSBvbiAoaWRlYWxseSkgaW5kZXBlbmRlbnQgZGF0YQoKCgo=